import $ from 'jquery';
import Module from './Module';

function array_merge(arr1, arr2) {
    arr1.push.apply(arr1, arr2);
    return arr1;
}

export default class Selection extends Module {

    _init() {
        this.editor = this._module;
        this._selection = document.getSelection();
        this.editor.on('selectionchanged', ((_this => e => {
            _this.reset();
            return _this._range = _this._selection.getRangeAt(0);
        }))(this));
        return this.editor.on('blur', ((_this => e => _this.reset()))(this));
    }
    reset() {
        this._range = null;
        this._startNodes = null;
        this._endNodes = null;
        this._containerNode = null;
        this._nodes = null;
        this._blockNodes = null;
        return this._rootNodes = null;
    }
    clear() {
        let e;
        try {
            this._selection.removeAllRanges();
        } catch (_error) {
            e = _error;
        }
        return this.reset();
    }
    range(range) {
        let ffOrIE;
        if (range) {
            this.clear();
            this._selection.addRange(range);
            this._range = range;
            ffOrIE = this.editor.util.browser.firefox || this.editor.util.browser.msie;
            if (!this.editor.inputManager.focused && ffOrIE) {
                this.editor.body.focus();
            }
        } else if (!this._range && this.editor.inputManager.focused && this._selection.rangeCount) {
            this._range = this._selection.getRangeAt(0);
        }
        return this._range;
    }
    startNodes() {
        if (this._range) {
            this._startNodes || (this._startNodes = ((_this => () => {
                let startNodes;
                startNodes = $(_this._range.startContainer).parentsUntil(_this.editor.body).get();
                startNodes.unshift(_this._range.startContainer);
                return $(startNodes);
            }))(this)());
        }
        return this._startNodes;
    }
    endNodes() {
        let endNodes;
        if (this._range) {
            this._endNodes || (this._endNodes = this._range.collapsed ? this.startNodes() : (endNodes = $(this._range.endContainer).parentsUntil(this.editor.body).get(), endNodes.unshift(this._range.endContainer), $(endNodes)));
        }
        return this._endNodes;
    }
    containerNode() {
        if (this._range) {
            this._containerNode || (this._containerNode = $(this._range.commonAncestorContainer));
        }
        return this._containerNode;
    }
    nodes() {
        if (this._range) {
            this._nodes || (this._nodes = ((_this => () => {
                let nodes;
                nodes = [];
                if (_this.startNodes().first().is(_this.endNodes().first())) {
                    nodes = _this.startNodes().get();
                } else {
                    _this.startNodes().each((i, node) => {
                        let $endNode;
                        let $node;
                        let $nodes;
                        let endIndex;
                        let index;
                        let sharedIndex;
                        let startIndex;
                        $node = $(node);
                        if (_this.endNodes().index($node) > -1) {
                            return nodes.push(node);
                        } else if ($node.parent().is(_this.editor.body) || (sharedIndex = _this.endNodes().index($node.parent())) > -1) {
                            if (sharedIndex && sharedIndex > -1) {
                                $endNode = _this.endNodes().eq(sharedIndex - 1);
                            } else {
                                $endNode = _this.endNodes().last();
                            }
                            $nodes = $node.parent().contents();
                            startIndex = $nodes.index($node);
                            endIndex = $nodes.index($endNode);
                            return array_merge(nodes, $nodes.slice(startIndex, endIndex).get());
                        } else {
                            $nodes = $node.parent().contents();
                            index = $nodes.index($node);
                            return array_merge(nodes, $nodes.slice(index).get());
                        }
                    });
                    _this.endNodes().each((i, node) => {
                        let $node;
                        let $nodes;
                        let index;
                        $node = $(node);
                        if ($node.parent().is(_this.editor.body) || _this.startNodes().index($node.parent()) > -1) {
                            nodes.push(node);
                            return false;
                        } else {
                            $nodes = $node.parent().contents();
                            index = $nodes.index($node);
                            return array_merge(nodes, $nodes.slice(0, index + 1));
                        }
                    });
                }
                return $($.unique(nodes));
            }))(this)());
        }
        return this._nodes;
    }
    blockNodes() {
        if (!this._range) {
            return;
        }
        this._blockNodes || (this._blockNodes = ((_this => () => _this.nodes().filter((i, node) => _this.editor.util.isBlockNode(node))))(this)());
        return this._blockNodes;
    }
    rootNodes() {
        if (!this._range) {
            return;
        }
        this._rootNodes || (this._rootNodes = ((_this => () => _this.nodes().filter((i, node) => {
            let $parent;
            $parent = $(node).parent();
            return $parent.is(_this.editor.body) || $parent.is('blockquote');
        })))(this)());
        return this._rootNodes;
    }
    rangeAtEndOf(node, range) {
        let afterLastNode;
        let beforeLastNode;
        let endNode;
        let endNodeLength;
        let lastNodeIsBr;
        let result;
        if (range == null) {
            range = this.range();
        }
        if (!(range && range.collapsed)) {
            return;
        }
        node = $(node)[0];
        endNode = range.endContainer;
        endNodeLength = this.editor.util.getNodeLength(endNode);
        beforeLastNode = range.endOffset === endNodeLength - 1;
        lastNodeIsBr = $(endNode).contents().last().is('br');
        afterLastNode = range.endOffset === endNodeLength;
        if (!((beforeLastNode && lastNodeIsBr) || afterLastNode)) {
            return false;
        }
        if (node === endNode) {
            return true;
        } else if (!$.contains(node, endNode)) {
            return false;
        }
        result = true;
        $(endNode).parentsUntil(node).addBack().each((i, n) => {
            let $lastChild;
            let beforeLastbr;
            let isLastNode;
            let nodes;
            nodes = $(n).parent().contents().filter(function() {
                return !(this !== n && this.nodeType === 3 && !this.nodeValue);
            });
            $lastChild = nodes.last();
            isLastNode = $lastChild.get(0) === n;
            beforeLastbr = $lastChild.is('br') && $lastChild.prev().get(0) === n;
            if (!(isLastNode || beforeLastbr)) {
                result = false;
                return false;
            }
        });
        return result;
    }
    rangeAtStartOf(node, range) {
        let result;
        let startNode;
        if (range == null) {
            range = this.range();
        }
        if (!(range && range.collapsed)) {
            return;
        }
        node = $(node)[0];
        startNode = range.startContainer;
        if (range.startOffset !== 0) {
            return false;
        }
        if (node === startNode) {
            return true;
        } else if (!$.contains(node, startNode)) {
            return false;
        }
        result = true;
        $(startNode).parentsUntil(node).addBack().each((i, n) => {
            let nodes;
            nodes = $(n).parent().contents().filter(function() {
                return !(this !== n && this.nodeType === 3 && !this.nodeValue);
            });
            if (nodes.first().get(0) !== n) {
                return result = false;
            }
        });
        return result;
    }
    insertNode(node, range) {
        if (range == null) {
            range = this.range();
        }
        if (!range) {
            return;
        }
        node = $(node)[0];
        range.insertNode(node);
        return this.setRangeAfter(node, range);
    }
    setRangeAfter(node, range) {
        if (range == null) {
            range = this.range();
        }
        if (range == null) {
            return;
        }
        node = $(node)[0];
        range.setEndAfter(node);
        range.collapse(false);
        return this.range(range);
    }
    setRangeBefore(node, range) {
        if (range == null) {
            range = this.range();
        }
        if (range == null) {
            return;
        }
        node = $(node)[0];
        range.setEndBefore(node);
        range.collapse(false);
        return this.range(range);
    }
    setRangeAtStartOf(node, range) {
        if (range == null) {
            range = this.range();
        }
        node = $(node).get(0);
        range.setEnd(node, 0);
        range.collapse(false);
        return this.range(range);
    }
    setRangeAtEndOf(node, range) {
        let $lastNode;
        let $node;
        let contents;
        let lastChild;
        let lastChildLength;
        let lastText;
        let nodeLength;
        if (range == null) {
            range = this.range();
        }
        $node = $(node);
        node = $node[0];
        if ($node.is('pre')) {
            contents = $node.contents();
            if (contents.length > 0) {
                lastChild = contents.last();
                lastText = lastChild.text();
                lastChildLength = this.editor.util.getNodeLength(lastChild[0]);
                if (lastText.charAt(lastText.length - 1) === '\n') {
                    range.setEnd(lastChild[0], lastChildLength - 1);
                } else {
                    range.setEnd(lastChild[0], lastChildLength);
                }
            } else {
                range.setEnd(node, 0);
            }
        } else {
            nodeLength = this.editor.util.getNodeLength(node);
            if (node.nodeType !== 3 && nodeLength > 0) {
                $lastNode = $(node).contents().last();
                if ($lastNode.is('br')) {
                    nodeLength -= 1;
                } else if ($lastNode[0].nodeType !== 3 && this.editor.util.isEmptyNode($lastNode)) {
                    $lastNode.append(this.editor.util.phBr);
                    node = $lastNode[0];
                    nodeLength = 0;
                }
            }
            range.setEnd(node, nodeLength);
        }
        range.collapse(false);
        return this.range(range);
    }
    deleteRangeContents(range) {
        let atEndOfBody;
        let atStartOfBody;
        let endRange;
        let startRange;
        if (range == null) {
            range = this.range();
        }
        startRange = range.cloneRange();
        endRange = range.cloneRange();
        startRange.collapse(true);
        endRange.collapse(false);
        atStartOfBody = this.rangeAtStartOf(this.editor.body, startRange);
        atEndOfBody = this.rangeAtEndOf(this.editor.body, endRange);
        if (!range.collapsed && atStartOfBody && atEndOfBody) {
            this.editor.body.empty();
            range.setStart(this.editor.body[0], 0);
            range.collapse(true);
            this.range(range);
        } else {
            range.deleteContents();
        }
        return range;
    }
    breakBlockEl(el, range) {
        let $el;
        if (range == null) {
            range = this.range();
        }
        $el = $(el);
        if (!range.collapsed) {
            return $el;
        }
        range.setStartBefore($el.get(0));
        if (range.collapsed) {
            return $el;
        }
        return $el.before(range.extractContents());
    }
    save(range) {
        let endCaret;
        let endRange;
        let startCaret;
        if (range == null) {
            range = this.range();
        }
        if (this._selectionSaved) {
            return;
        }
        endRange = range.cloneRange();
        endRange.collapse(false);
        startCaret = $('<span/>').addClass('simditor-caret-start');
        endCaret = $('<span/>').addClass('simditor-caret-end');
        endRange.insertNode(endCaret[0]);
        range.insertNode(startCaret[0]);
        this.clear();
        return this._selectionSaved = true;
    }
    restore() {
        let endCaret;
        let endContainer;
        let endOffset;
        let range;
        let startCaret;
        let startContainer;
        let startOffset;
        if (!this._selectionSaved) {
            return false;
        }
        startCaret = this.editor.body.find('.simditor-caret-start');
        endCaret = this.editor.body.find('.simditor-caret-end');
        if (startCaret.length && endCaret.length) {
            startContainer = startCaret.parent();
            startOffset = startContainer.contents().index(startCaret);
            endContainer = endCaret.parent();
            endOffset = endContainer.contents().index(endCaret);
            if (startContainer[0] === endContainer[0]) {
                endOffset -= 1;
            }
            range = document.createRange();
            range.setStart(startContainer.get(0), startOffset);
            range.setEnd(endContainer.get(0), endOffset);
            startCaret.remove();
            endCaret.remove();
            this.range(range);
        } else {
            startCaret.remove();
            endCaret.remove();
        }
        this._selectionSaved = false;
        return range;
    }
}
Selection.pluginName = 'Selection';
Selection.prototype._range = null;
Selection.prototype._startNodes = null;
Selection.prototype._endNodes = null;
Selection.prototype._containerNode = null;
Selection.prototype._nodes = null;
Selection.prototype._blockNodes = null;
Selection.prototype._rootNodes = null;